PR: Refactoring PyOP3 Codegen - #5392
Conversation
- Goal: Create an interface to a code generation context (MLIR or Loopy) - Status: Interface created, battling PETSc bug before cleaning more. - Goal: Integrate MLIR for auto-generation - Status: Was working but transitioning to pyop3->mlir pipeline as opposed to pym->mlir. Refactoring process is ongoing.
requirements tracking pyop3 && typing hints
Merged Connor's update with new setup. Current issue with values loaded into the buffers. Occurs even while using old lower/loopy.py Debugging in process.
PyOP3 updates and path traversals reflected in the new lowering structure
Still need to integrate dtype support for all op3 expressions. Removed abstractmethod while testing.
connorjward
left a comment
There was a problem hiding this comment.
Key point on an initial first pass is I would like to limit the scope of what the context objects know about.
| else: | ||
| cs_expr = (insn,) | ||
|
|
||
| if compiler_parameters.codegen == "loopy": |
There was a problem hiding this comment.
I think I prefer 'backend' to 'codegen'
There was a problem hiding this comment.
That's fine, I will switch. Was only wary that backend is used often but agree that it makes more sense.
There was a problem hiding this comment.
Terminology is really confusing around here. We have:
- compile
- codegen
- lower
which are all in some sense interchangeable. I really want to use "compile" as the single term for everything but this conflicts with the other sense of "compiler" (i.e. GCC etc).
I think in my main branch I will do the following renaming:
pyop3/lowertopyop3/compilepyop3/compile.pytopyop3/cc.py
File naming isn't important for this PR, but I think I will make that change soon and you may hit git conflicts.
| return ctx | ||
|
|
||
|
|
||
| # NOTE: Not a big fan of how compile sits in this file. |
There was a problem hiding this comment.
Yeah I would expect to put it in codegen.py. This file can just contain the abstract class.
There was a problem hiding this comment.
Moved _compile to codegen.py.
Only point of contention - and why it was not already there - is that parse_loop_properly_this_time uses _compile in the context.py file. As such, _compile now has to be imported. This must be a local import, to avoid a circular import.
It feels like a code smell but it does work.
Possible steps for resolving the potential smell would be:
- Move
_compilefunction to another separate file (I think this would bloat the directory) - Rewrite
parse_loop_properly_this_time
Else, we can leave it for later, it works fine. May just be a micro-optimisation.
There was a problem hiding this comment.
parse_loop_properly_this_time (which I think I would like you to rename) is another function that I don't think should belong to the context and ideally could be rewritten in a backend agnostic fashion.
|
|
||
| return indices | ||
|
|
||
| def compile_standalone_function( |
There was a problem hiding this comment.
When I designed the 'context' class I imagined that it would be entirely independent from any of the pyop3 'language'. It would only see the buffers. For example here I think we could amend the add_function_call to take in more arguments. The visitor for when we hit standalone function can then be
args = [(a.buffer_view, spec.intent) for a, spec in zip(call.arguments, call.argspec, strict=True)]
context.add_function_call(call.function.code, args)The crucial points are:
- The context never sees the
StandaloneCalledFunctiontype - The visitor for
StandaloneCalledFunctioncan remain generic for different backends
There was a problem hiding this comment.
I have made some notable changes to follow this idea.
_compile_array_assignmentand_compile_loop(formerly_parse_loop...) are now generic incodegen.py_compile_petsc_matand_compile_exscanmoved back tocodegen.py.- Context classes now have abstract methods
add_petsc_mat,add_exscanfor backend-specific additions.
loopy.py should now be working with buffer_views as opposed to pyop3 terminology, where possible.
Things that I am conscious of:
add_petsc_mathas bloated function definition to avoid passing pyop3 language.compile_exscancan almost be made agnostic but not until I develop an equivalent topym.substitutefor MLIR gen.
There was a problem hiding this comment.
Context classes now have abstract methods add_petsc_mat, add_exscan for backend-specific additions.
I would suggest not bothering with this and just leave the 'generic' routines as they are (i.e. loopy specific). I don't think we care about having MLIR for either of them so just do:
def _compile_petsc_mat(..., ctx):
if not isinstance(ctx, LoopyCodegenContext):
raise NotImplementedError
# loopy-specific codeThere was a problem hiding this comment.
This makes sense (and my life easier). Pushed these changes
- rename compiler option `codegen` -> `backend` - documenting `backend` compiler option - functional variable renaming
- moving dispatch _compile function from context.py -> codegen.py - local import in _parse_loop to avoid circular import - moving dispatch _collect_temporary_shapes from context.py -> transform.py
- codegen holds more pyop3 traversal. - backend-specific functions introduced where necessary notable flaw is bloated function call for add_petsc_mat.
- not implementing methods for MLIR, loopy-only.
connorjward
left a comment
There was a problem hiding this comment.
I think the structure is very close now. I am not striving for perfection given the rough nature of what you started with, but I think we should make sure that we get a decent chunk of the core design right.
|
|
||
| self.add_assignment(lexpr, rexpr) | ||
|
|
||
| def lower_expr(self, expr, iname_maps, loop_indices, *, intent=READ, paths=None) -> pym.Expression: |
There was a problem hiding this comment.
I think this is a design issue. We need the codegen context to know how to lower expressions, because it differs between backends, but the logic should really be in another class (LoopyExpressionLowerer(ExpressionLowerer) or LoopyExpressionCompiler(AbstractExpressionCompiler)).
For now the easiest thing to do is just have:
def _lower_expr(...): # a free function
...
class LoopyCodegenContext(...):
...
def lower_expr(self, ...):
return _lower_expr(..., ctx=self)There was a problem hiding this comment.
It does feel bloated in loopy.py. I was conscious of making too many classes.
I think I will revisit making another class once I have a working MLIR lowering implementation.
For now, I have just made this a free function, as suggested.
as title
- moving lower_expr to free function
- going to revisit this once I have a working MLIR lowering implementation
Refactoring
pyop3/lower/PR presents abstraction in the
lower/design to allow for alternative code generation backends.Principally,
loopy.pyhas been broken into three separate files:codegen.py- context orchestrator which returns lowered IR back to translation layercontext.py- generic class and high-level traversal of axis trees to generate pyop3 expressions.(possible that class and traversal has too many responsibilities).
loopy.py-implements backend-specific lowering from pyop3 to respective target IRsThis PR serves as a stepping stone to the introduction of MLIR as a code generation backend.
A new
mlir.pymodule will subclasscontext.pythat will construct MLIR from PyOP3 expressions, using xDSL.